// Copyright (c) 2009-2015 Intel Corporation
// All rights reserved.
//
// WARRANTY DISCLAIMER
//
// THESE MATERIALS ARE PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR ITS
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THESE
// MATERIALS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Intel Corporation is the author of the Materials, and requests that all
// problem reports or change requests be submitted to it directly

#include "OCL.h"

#include <vector>
#include <iterator>

//basic components of any cl sample application
cl_context g_clContext;
cl_platform_id g_platformToUse; //TODO: may be able to remove this
cl_device_id *g_clDevices = NULL;
cl_command_queue g_clCommandQueue;
cl_program g_clProgram;
char *g_clProgramString = NULL;
cl_kernel cl_kernel_drawBox_Y;
cl_kernel cl_kernel_drawBox_UV;

//FOR SHARING, because sharing is caring
cl_mem	g_SharedNV12imageCLMemObject_Y;
cl_mem	g_SharedNV12imageCLMemObject_UV;
bool	g_UseD3D11_RESOURCE_MISC_SHAREDflag;

//pointers to the extension functions
clCreateFromD3D11Texture2DKHR_fn   ptrToFunction_clCreateFromD3D11Texture2DKHR = NULL;
clEnqueueAcquireD3D11ObjectsKHR_fn ptrToFunction_clEnqueueAcquireD3D11ObjectsKHR = NULL;
clEnqueueReleaseD3D11ObjectsKHR_fn ptrToFunction_clEnqueueReleaseD3D11ObjectsKHR = NULL;

int UseD3D11_RESOURCE_MISC_SHAREDFlagCL()
{
	int status = FAIL;
	cl_bool result;

	//query opencl for the rseult of this flag off of the context
	//note we can't get here without support of this extension so it is okay to query for it directly
	status = clGetContextInfo(g_clContext, CL_CONTEXT_D3D11_PREFER_SHARED_RESOURCES_KHR, sizeof(cl_bool), &result, NULL);
	if(result == CL_TRUE)
	{
		printf("OpenCL reports that creating the surface with D3D11_RESOURCE_MISC_SHARED flag will result in better performance\n");
		g_UseD3D11_RESOURCE_MISC_SHAREDflag = true;
		status = SUCCESS;
	}
	else
	{
		printf("Assuming I will not create the D3D11 surface with the D3D11_RESOURCE_MISC_SHARED flag\n");
		g_UseD3D11_RESOURCE_MISC_SHAREDflag = false;
		status = SUCCESS;
	}

	return status;
}
int VerifyCLDX11SurfaceSharingPlatformAvailableCL()
{
	int status = 0;
	cl_uint numPlatforms = 0;

	status = clGetPlatformIDs(0, NULL, &numPlatforms);
	testStatus(status, "clGetPlatformIDs error\n");
	cl_platform_id *platforms = (cl_platform_id *)malloc(sizeof(cl_platform_id) * numPlatforms);
	if(platforms == NULL)
	{
		printf("Error when allocating space for the platforms\n");
		exit(EXIT_FAILURE);
	}

	status = clGetPlatformIDs(numPlatforms, platforms, NULL);
	testStatus(status, "clGetPlatformIDs error");

	for(unsigned int i=0;i<numPlatforms;i++)
	{
		printf("******************************************************************************\n");
		char platformVendor[100];
		memset(platformVendor, '\0',100);
		status = clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, sizeof(platformVendor), platformVendor, NULL);
		testStatus(status, "clGetPlatformInfo error");
		
		char platformName[100];
		memset(platformName, '\0',100);
		status = clGetPlatformInfo(platforms[i], CL_PLATFORM_NAME, sizeof(platformName), platformName, NULL);
		testStatus(status, "clGetPlatformInfo error");

		char extension_string[2048];
		memset(extension_string, '\0', 2048);
		status = clGetPlatformInfo(platforms[i], CL_PLATFORM_EXTENSIONS, sizeof(extension_string), extension_string, NULL);
		//printf("Extensions supported: %s\n", extension_string);
	
		char *extStringStart = NULL;
		//printf("Determining if %s from %s supports cl_khr_d3d11_sharing extension...\n", platformName, platformVendor);
		extStringStart = strstr(extension_string, "cl_khr_d3d11_sharing");
		if(extStringStart != 0)
		{
			printf("Platform %s does support cl_khr_d3d11_sharing\n", platformName);

			//now that we found a platform that supports CL/DX11 sharing, let's report each device in the platform that supports sharing
			//get number of devices in the platform
			//for each platform, query extension string
			cl_uint num_devices = 0;
			status = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, 0, NULL, &num_devices);
			testStatus(status, "Error getting number of devices\n");

			cl_device_id *clDevices = NULL;
			clDevices = (cl_device_id *)malloc(sizeof(cl_device_id)*num_devices);
			if(clDevices == NULL)
			{
				printf("Error when allocating\n");
			}
			status = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, num_devices, clDevices, 0);
			testStatus(status, "clGetDeviceIDs error");

			for(cl_uint iDevNum=0;iDevNum<num_devices;iDevNum++)
			{
				//query each for their extension string
				//print out the device type just to make sure we got it right
				cl_device_type device_type;
				char vendorName[256];
				memset(vendorName, '\0', 256);
				char devExtString[2048];
				memset(devExtString, '\0', 2048);

				clGetDeviceInfo(clDevices[iDevNum], CL_DEVICE_TYPE, sizeof(cl_device_type), (void *)&device_type, NULL);
				clGetDeviceInfo(clDevices[iDevNum], CL_DEVICE_VENDOR, (sizeof(char)*256), &vendorName, NULL);
				clGetDeviceInfo(clDevices[iDevNum], CL_DEVICE_EXTENSIONS, (sizeof(char)*2048), &devExtString, NULL);

				char *extStringStart1 = NULL;
				extStringStart1 = strstr(devExtString, "cl_khr_d3d11_sharing");
		
				char devTypeString[256];
				memset(devTypeString, '\0', 256);

				if(extStringStart1 != 0)
				{
					if(device_type == CL_DEVICE_TYPE_CPU)
					{
						strcpy_s(devTypeString, "CPU");
					}
					else if(device_type == CL_DEVICE_TYPE_GPU)
					{
						strcpy_s(devTypeString, "GPU");
					}
					else
					{
						strcpy_s(devTypeString, "Not a CPU or GPU"); //for sample code, not product
					}
					printf("Device %s in %s platform supports CL/DX11 sharing\n", devTypeString, vendorName);
				}
				else
				{
					printf("Device %s in %s platform does not support CL/DX11 sharing\n", devTypeString, vendorName);
				}


				char *extStringStart2 = NULL;
				extStringStart2 = strstr(devExtString, "cl_intel_device_attribute_query");

				if (extStringStart2 != 0)
				{
					if (device_type == CL_DEVICE_TYPE_CPU)
					{
						strcpy_s(devTypeString, "CPU");
					}
					else if (device_type == CL_DEVICE_TYPE_GPU)
					{
						strcpy_s(devTypeString, "GPU");
					}
					else
					{
						strcpy_s(devTypeString, "Not a CPU or GPU"); //for sample code, not product
					}
					printf("Device %s in %s platform supports cl_intel_device_attribute_query extension\n", devTypeString, vendorName);
				}
				else
				{
					printf("Device %s in %s platform does not support cl_intel_device_attribute_query extension\n", devTypeString, vendorName);
				}
 			} //end for(...)
			free(clDevices);
		}
		else
		{
			printf("\nPlatform %s does not support cl_khr_d3d11_sharing\n\n",platformName);
		}
	}
	
	printf("Sample only verified to run on Intel processor graphics\n");
	printf("******************************************************************************\n");

	return status;
}


void SimulateCL()
{
	cl_int status;
	static float fDimmerSwitch = 0.0f;  //Y = 255 * fDimmerSwitch
	cl_int top = 16;
	cl_int left = 64;

	//see page 107 of rev21 of the spec, clearly states this won't return until all D3D11 has completed
	status = ptrToFunction_clEnqueueAcquireD3D11ObjectsKHR(g_clCommandQueue, 1, &g_SharedNV12imageCLMemObject_Y, 0, 0, 0);

	status = ptrToFunction_clEnqueueAcquireD3D11ObjectsKHR(g_clCommandQueue, 1, &g_SharedNV12imageCLMemObject_UV, 0, 0, 0);


	status = clSetKernelArg(cl_kernel_drawBox_Y, 0, sizeof(cl_mem), &g_SharedNV12imageCLMemObject_Y);
	testStatus(status, "clSetKernelArg");
	status = clSetKernelArg(cl_kernel_drawBox_Y, 1, sizeof(cl_int), &top);
	status = clSetKernelArg(cl_kernel_drawBox_Y, 2, sizeof(cl_int), &left);
	status = clSetKernelArg(cl_kernel_drawBox_Y, 3, sizeof(cl_float), &fDimmerSwitch);
	testStatus(status, "clSetKernelArg"); 

	status = clSetKernelArg(cl_kernel_drawBox_UV, 0, sizeof(cl_mem), &g_SharedNV12imageCLMemObject_UV);
	status = clSetKernelArg(cl_kernel_drawBox_UV, 1, sizeof(cl_int), &top);
	status = clSetKernelArg(cl_kernel_drawBox_UV, 2, sizeof(cl_int), &left);
	status = clSetKernelArg(cl_kernel_drawBox_UV, 3, sizeof(cl_float), &fDimmerSwitch);

	size_t global_dim[2];
	global_dim[0] = SHARED_IMAGE_HEIGHT;
	global_dim[1] = SHARED_IMAGE_WIDTH;

	status = clEnqueueNDRangeKernel(g_clCommandQueue, cl_kernel_drawBox_Y, 2, NULL, global_dim, NULL, 0, NULL, NULL);
	testStatus(status, "clEnqueueNDRangeKernel fail");
	status = clEnqueueNDRangeKernel(g_clCommandQueue, cl_kernel_drawBox_UV, 2, NULL, global_dim, NULL, 0, NULL, NULL);

	status = ptrToFunction_clEnqueueReleaseD3D11ObjectsKHR(g_clCommandQueue, 1, &g_SharedNV12imageCLMemObject_Y, 0, NULL, NULL);
	testStatus(status, "Fail on clEnqueueReleaseD3D11ObjectsKHR");
	status = ptrToFunction_clEnqueueReleaseD3D11ObjectsKHR(g_clCommandQueue, 1, &g_SharedNV12imageCLMemObject_UV, 0, NULL, NULL);
	clFinish(g_clCommandQueue);

	fDimmerSwitch += .010f;
	if(fDimmerSwitch > 1.0)
	{
		fDimmerSwitch = 0.0f;
	}

}


//if an error occurs we exit
//it would be better to cleanup state then exit, for sake of simplicity going to omit the cleanup
void testStatus(int status, char *errorMsg)
{
	if(status != SUCCESS)
	{
		if(errorMsg == NULL)
		{
			printf("Error\n");
		}
		else
		{
			printf("Error: %s", errorMsg);
		}
		exit(EXIT_FAILURE);
	}
}

int HandleCompileError(void)
{
	cl_int logStatus;
	char *buildLog = NULL;
	size_t buildLogSize = 0;
	//in this tutorial i only have one device
	logStatus = clGetProgramBuildInfo( g_clProgram, g_clDevices[0], CL_PROGRAM_BUILD_LOG, buildLogSize, buildLog, &buildLogSize);
	if(logStatus != CL_SUCCESS)
	{
		printf("logging error\n");
		exit(EXIT_FAILURE);
	}

	buildLog = (char *)malloc(buildLogSize);
	if(buildLog == NULL)
	{
		printf("ERROR TO ALLOCATE MEM FOR BUILDLOG\n");
		exit(EXIT_FAILURE);
	}

	memset(buildLog, 0, buildLogSize);

	logStatus = clGetProgramBuildInfo (g_clProgram, g_clDevices[0], CL_PROGRAM_BUILD_LOG, buildLogSize, buildLog, NULL);
	if(logStatus != CL_SUCCESS)
	{
		free(buildLog);
		return FAIL;
	}

	printf("\nBUILD LOG\n");
	printf("************************************************************************\n");
	printf("%s END \n", buildLog);
	printf("************************************************************************\n");
	free(buildLog);
	return SUCCESS;
}


//convert to string needs to take a string a file as input and write a char * to output
int convertToStringBuf(const char *fileName)
{
	FILE *fp = NULL;
	int status;

	errno_t err = fopen_s(&fp, fileName, "r");
	if(err != 0)
	{
		printf("Error opening %s, check path\n", fileName);
		exit(EXIT_FAILURE);
	}

	status = fseek(fp, 0, SEEK_END);
	if(status != 0)
	{
		printf("Error finding end of file\n");
		exit(EXIT_FAILURE);
	}

	int len = ftell(fp);
	if(len == -1L)
	{
		printf("Error reporting position of file pointer\n");
		exit(EXIT_FAILURE);
	}
	rewind(fp);
	g_clProgramString = (char *)malloc((len * sizeof(char))+1);
	if(g_clProgramString == NULL)
	{
		printf("Error in allocation when converting CL source file to a string\n");
		exit(EXIT_FAILURE);
	}
	memset(g_clProgramString, '\0', len+1);
	fread(g_clProgramString, sizeof(char), len, fp);
	status = ferror(fp);
	if(status != 0)
	{
		printf("Error reading into the program string from file\n");
		exit(EXIT_FAILURE);
	}
	fclose(fp);
	
	return SUCCESS;
}


int ShareDX11BufferWithCL(ID3D11Texture2D* g_pSharedDX11Texture2D)
{
	int status = 0;

	g_SharedNV12imageCLMemObject_Y = NULL;
	g_SharedNV12imageCLMemObject_UV = NULL;

	//g_SharedNV12imageCLMemObject = ptrToFunction_clCreateFromD3D11Texture2DKHR(g_clContext, CL_MEM_READ_WRITE, g_pSharedDX11Texture2D, 0, &status);

	g_SharedNV12imageCLMemObject_Y = ptrToFunction_clCreateFromD3D11Texture2DKHR(g_clContext, CL_MEM_READ_WRITE, g_pSharedDX11Texture2D, 0, &status); //this is for Y
	if(status == 0)
	{
		//printf("Successfully shared!\n");
		status = SUCCESS;
	}
	else
	{
		printf("Sharing failed\n");
		status = FAIL;
	}

	g_SharedNV12imageCLMemObject_UV = ptrToFunction_clCreateFromD3D11Texture2DKHR(g_clContext, CL_MEM_READ_WRITE, g_pSharedDX11Texture2D, 1, &status); //this is for UV
	if (status == 0)
	{
		//printf("Successfully shared!\n");
		status = SUCCESS;
	}
	else
	{
		printf("Sharing failed 1\n");
		status = FAIL;
	}
	return status;
}

int ReleaseSharedDX11BufferWithCL()
{
	//cleanup all CL queues, contexts, programs, mem_objs
	cl_int status;

	if(g_SharedNV12imageCLMemObject_Y != NULL)
	{
		status = clReleaseMemObject(g_SharedNV12imageCLMemObject_Y);
		testStatus(status, "Error releasing mem object");
		g_SharedNV12imageCLMemObject_Y = NULL;
	}
	if (g_SharedNV12imageCLMemObject_UV != NULL)
	{
		status = clReleaseMemObject(g_SharedNV12imageCLMemObject_UV);
		testStatus(status, "Error releasing mem object 1");
		g_SharedNV12imageCLMemObject_UV = NULL;
	}
	return SUCCESS;
}


int InitDeviceCodeCL()
{
	cl_int status;

	//load CL file, build CL program object, create CL kernel object
	const char *filename = CL_KERNELS;

	status = convertToStringBuf(filename);

	size_t sourceSize = strlen(g_clProgramString);

	g_clProgram = clCreateProgramWithSource(g_clContext, 1, (const char **)&g_clProgramString, &sourceSize, &status);
	testStatus(status, "clCreateProgramWithSource error");

	status = clBuildProgram(g_clProgram, 1, g_clDevices, NULL, NULL, NULL);
	if(status != CL_SUCCESS)
	{
		if(status == CL_BUILD_PROGRAM_FAILURE)
		{
			HandleCompileError();
		} //end if BUILD_PROGRAM_FAILURE
	} //end if CL_SUCCESS

	cl_kernel_drawBox_Y = clCreateKernel(g_clProgram, "drawBox_Y", &status);
	testStatus(status, "clCreateKernel error");
	cl_kernel_drawBox_UV = clCreateKernel(g_clProgram, "drawBox_UV", &status);
	testStatus(status, "clCreateKernel error 1");

	return SUCCESS;

}


const char* oclImageFormatString(cl_uint uiImageFormat)
{
	// cl_channel_order 
	if (uiImageFormat == CL_R)return "CL_R";
	if (uiImageFormat == CL_A)return "CL_A";
	if (uiImageFormat == CL_RG)return "CL_RG";
	if (uiImageFormat == CL_RA)return "CL_RA";
	if (uiImageFormat == CL_RGB)return "CL_RGB";
	if (uiImageFormat == CL_RGBA)return "CL_RGBA";
	if (uiImageFormat == CL_BGRA)return "CL_BGRA";
	if (uiImageFormat == CL_ARGB)return "CL_ARGB";
	if (uiImageFormat == CL_INTENSITY)return "CL_INTENSITY";
	if (uiImageFormat == CL_LUMINANCE)return "CL_LUMINANCE";

	// cl_channel_type 
	if (uiImageFormat == CL_SNORM_INT8)return "CL_SNORM_INT8";
	if (uiImageFormat == CL_SNORM_INT16)return "CL_SNORM_INT16";
	if (uiImageFormat == CL_UNORM_INT8)return "CL_UNORM_INT8";
	if (uiImageFormat == CL_UNORM_INT16)return "CL_UNORM_INT16";
	if (uiImageFormat == CL_UNORM_SHORT_565)return "CL_UNORM_SHORT_565";
	if (uiImageFormat == CL_UNORM_SHORT_555)return "CL_UNORM_SHORT_555";
	if (uiImageFormat == CL_UNORM_INT_101010)return "CL_UNORM_INT_101010";
	if (uiImageFormat == CL_SIGNED_INT8)return "CL_SIGNED_INT8";
	if (uiImageFormat == CL_SIGNED_INT16)return "CL_SIGNED_INT16";
	if (uiImageFormat == CL_SIGNED_INT32)return "CL_SIGNED_INT32";
	if (uiImageFormat == CL_UNSIGNED_INT8)return "CL_UNSIGNED_INT8";
	if (uiImageFormat == CL_UNSIGNED_INT16)return "CL_UNSIGNED_INT16";
	if (uiImageFormat == CL_UNSIGNED_INT32)return "CL_UNSIGNED_INT32";
	if (uiImageFormat == CL_HALF_FLOAT)return "CL_HALF_FLOAT";
	if (uiImageFormat == CL_FLOAT)return "CL_FLOAT";

	// unknown constant
	return "Unknown";
}


#define OPENCL_CHECK_ERRORS(ERR)        \
    if(ERR != CL_SUCCESS)                  \
    {                                      \
    std::cerr                                   \
    << "OpenCL error with code " << ERR    \
    << " happened in file " << __FILE__    \
    << " at line " << __LINE__             \
    << ". Exiting...\n";                   \
    exit(1);                               \
    }


unsigned char temp_video_buffer[1920 * 1088 * 4];

int InitCL()
{
	cl_int status = 0;
	cl_uint numPlatforms= 0;

	std::vector < cl_platform_id> intel_platforms;

	intel_platforms.empty();

	//[1] get the platform
	g_platformToUse = NULL;
	status = clGetPlatformIDs(0, NULL, &numPlatforms);
	testStatus(status, "clGetPlatformIDs error");
	
	cl_platform_id *platforms = (cl_platform_id *)malloc(sizeof(cl_platform_id) * numPlatforms);
	if(platforms == NULL)
	{
		printf("Error when allocating space for the platforms\n");
		exit(EXIT_FAILURE);
	}

	status = clGetPlatformIDs(numPlatforms, platforms, NULL);
	testStatus(status, "clGetPlatformIDs error");
	printf("Total numPlatforms found = %d\n", numPlatforms);

	//[2] get device ids for the platform i have obtained
	cl_uint num_devices = -1; //yeah yeah, i know uint 
	cl_device_info devTypeToUse = CL_DEVICE_CPU_OR_GPU;  //set as CPU or GPU from host_common.h	
	for(unsigned int i=0;i<numPlatforms;i++)
	{
		char pbuf[256];
		status = clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, sizeof(pbuf), pbuf, NULL);
		testStatus(status, "clGetPlatformInfo error");

		//leaving this strcmp for AMD, used when debugging and is the precise AMD platform name
		//if(!strcmp(pbuf, "Advanced Micro Devices, Inc.")) 
		//We found the Intel Platform, this is the one we wantto use
		if(!strcmp(pbuf, "Intel(R) Corporation"))
		{
			printf("Great! We found an Intel OpenCL platform. - platform index[%d]\n",i);
			g_platformToUse = platforms[i];
			status = clGetDeviceIDs(g_platformToUse, devTypeToUse, 0, g_clDevices, &num_devices);
			printf("	platform index[%d] : num_devices = %d\n", i, num_devices);
			//if (status == CL_DEVICE_NOT_FOUND)
			//	continue; 
			//else
			//	break;

			intel_platforms.push_back(platforms[i]);

			if (status == CL_DEVICE_NOT_FOUND)
			{
				printf("	[%s][line:%n]CL_DEVICE_NOT_FOUND!!!\n",__FILE__,__LINE__);
				continue;
			}
			else
			{
				if (g_platformToUse == NULL)
				{
					printf("	***We have not found an Intel(r) OpenCL implementation, exiting application\n");
					exit(EXIT_FAILURE);
				}

				cl_platform_id dummy_g_platformToUse = g_platformToUse;
				cl_device_id *dummy_g_clDevices = NULL;
				cl_uint dummy_num_devices = num_devices;

				//allocate space
				dummy_g_clDevices = (cl_device_id *)malloc(sizeof(cl_device_id)*dummy_num_devices);
				if (dummy_g_clDevices == NULL)
				{
					printf("		***Error when creating space for devices\n");
					exit(EXIT_FAILURE);
				}
				//we know we have an intel platform, get the device we want to use
				status = clGetDeviceIDs(dummy_g_platformToUse, devTypeToUse, dummy_num_devices, dummy_g_clDevices, 0);
				testStatus(status, "		***clGetDeviceIDs error");

				cl_int dummy_device_id;
				for (dummy_device_id = 0; dummy_device_id < dummy_num_devices; dummy_device_id++)
				{
					//print out the device type just to make sure we got it right
					cl_device_type device_type;
					cl_uint device_vendor_id;
					cl_uint device_device_id;
					char vendorName[256];
					char DeviceVersion[1024];
					memset(vendorName, '\0', 256);
					memset(DeviceVersion, '\0', 1024);

#define CL_DEVICE_ID_INTEL                        0x4251

					clGetDeviceInfo(dummy_g_clDevices[dummy_device_id], CL_DEVICE_TYPE, sizeof(cl_device_type), (void *)&device_type, NULL);
					clGetDeviceInfo(dummy_g_clDevices[dummy_device_id], CL_DEVICE_VENDOR_ID, sizeof(cl_uint), (void *)&device_vendor_id, NULL);
					clGetDeviceInfo(dummy_g_clDevices[dummy_device_id], CL_DEVICE_ID_INTEL, sizeof(cl_uint), (void *)&device_device_id, NULL);

					clGetDeviceInfo(dummy_g_clDevices[dummy_device_id], CL_DEVICE_VENDOR, (sizeof(char) * 256), vendorName, NULL);
					clGetDeviceInfo(dummy_g_clDevices[dummy_device_id], CL_DEVICE_VERSION, (sizeof(char) * 1024), DeviceVersion, NULL);



					if (device_type == CL_DEVICE_TYPE_CPU)
					{
						printf("		[%d] Device type is CPU, Vendor is %s\n", dummy_device_id, vendorName);
					}
					else if (device_type == CL_DEVICE_TYPE_GPU)
					{
						printf("		[%d] Device type is GPU, Vendor is %s\n", dummy_device_id, vendorName);
					}
					else
					{
						printf("		[%d] device type is unknown\n",dummy_device_id );
					}

					clGetDeviceInfo(dummy_g_clDevices[0], CL_DEVICE_NAME, (sizeof(char) * 256), vendorName, NULL);
					printf("		[%d] Device name is %s\n", dummy_device_id, vendorName);
					printf("		[%d] Device_vendor_id is 0x%08X\n", dummy_device_id, device_vendor_id);
					printf("		[%d] device_device_id is 0x%08X\n", dummy_device_id, device_device_id);
					printf("		[%d] CL_DEVICE_VERSION is %s\n", dummy_device_id, DeviceVersion);
				}


				if (dummy_num_devices > 0)
				{
					free(dummy_g_clDevices);
				}
			}
		}
	}
	printf("\n");

	/************************************************************/

	/******************************************************************/
	if (intel_platforms.size() == 0)
	{
		//This code for intel GPU only
		printf("We have not found an Intel(r) OpenCL implementation, exiting application\n");
		exit(EXIT_FAILURE);
	}

	cl_uint numDevs = 0;
	clGetDeviceIDsFromD3D11KHR_fn ptrToFunction_clGetDeviceIDsFromD3D11KHR = NULL;
	cl_context_properties cps[] =
	{
		CL_CONTEXT_PLATFORM, (cl_context_properties)NULL,
		CL_CONTEXT_D3D11_DEVICE_KHR, (intptr_t)g_pd3dDevice,
		CL_CONTEXT_INTEROP_USER_SYNC, CL_FALSE,
		0
	};

	cl_int platform_index;
	for (cl_platform_id intel_platform : intel_platforms)
	{
		g_platformToUse = intel_platform;

		cps[1] = (cl_context_properties)g_platformToUse;

		ptrToFunction_clGetDeviceIDsFromD3D11KHR = NULL;
		ptrToFunction_clGetDeviceIDsFromD3D11KHR = (clGetDeviceIDsFromD3D11KHR_fn)clGetExtensionFunctionAddressForPlatform(g_platformToUse, "clGetDeviceIDsFromD3D11KHR");

		numDevs = 0;
		//size_t bytes = 0;
		//careful with the g_pd3DDevice
		status = ptrToFunction_clGetDeviceIDsFromD3D11KHR(g_platformToUse, CL_D3D11_DEVICE_KHR, (void *)g_pd3dDevice, CL_PREFERRED_DEVICES_FOR_D3D11_KHR, 0, NULL, &numDevs);
		printf("clGetDeviceIDsFromD3D11KHR returns status = %d\n", status);

		std::vector <cl_platform_id> ::iterator itr;
		itr = find(intel_platforms.begin(), intel_platforms.end(), intel_platform);
		cl_int platform_index = distance(intel_platforms.begin(), itr);

		if (status != CL_SUCCESS)
		{
			printf("Failed: the [%d/%d] OCL platform unmatch\n", platform_index + 1, intel_platforms.size());
		}
		else
		{
			printf("Good: Found the matched OCL platform [%d/%d]\n", platform_index + 1, intel_platforms.size());
			break;
		}

	}


	testStatus(status, "Failed on clGetDeviceIDsFromD3D11KHR");

	cl_device_id *devID = NULL;
	g_clDevices = (cl_device_id *)malloc(sizeof(cl_device_id) * numDevs);
	ptrToFunction_clGetDeviceIDsFromD3D11KHR(g_platformToUse, CL_D3D11_DEVICE_KHR, (void *)g_pd3dDevice, CL_PREFERRED_DEVICES_FOR_D3D11_KHR, numDevs, g_clDevices, NULL);
	testStatus(status, "Failed on clGetDeviceIDsFromD3D11KHR");

	//create an OCL context from the device we are using as our DX11 rendering device
	g_clContext = clCreateContext(cps, 1, g_clDevices, NULL, NULL, &status);
	testStatus(status, "clCreateContext error");

	//create an openCL commandqueue
	//here i should be smarter but because i know this is only to run on Intel GPU I make an assumption to create
	//the queue and move on, the sample is about sharing, not about robust device call/response/create patterns
	g_clCommandQueue = clCreateCommandQueue(g_clContext, g_clDevices[0], 0, &status);
	testStatus(status, "clCreateCommandQueue error");
	
	//create device side program, compile and create program objects
	status = InitDeviceCodeCL();
	testStatus(status, "error in initDevice()");


#ifdef GET_CL_IMG_FORMAT
	// Determine and show image format support 
	cl_uint uiNumSupportedFormats = 0;

	// 2D
	clGetSupportedImageFormats(g_clContext, CL_MEM_READ_WRITE,
		CL_MEM_OBJECT_IMAGE2D,
		NULL, NULL, &uiNumSupportedFormats);
	cl_image_format* ImageFormats = new cl_image_format[uiNumSupportedFormats];
	clGetSupportedImageFormats(g_clContext, CL_MEM_READ_WRITE,
		CL_MEM_OBJECT_IMAGE2D,
		uiNumSupportedFormats, ImageFormats, NULL);
	printf("	clGetSupportedImageFormats(context, CL_MEM_READ_WRITE,\
		CL_MEM_OBJECT_IMAGE2D,\
		uiNumSupportedFormats, ImageFormats, NULL); \n\n");
	printf("  ---------------------------------\n");
	printf("  2D Image Formats Supported (%u)\n", uiNumSupportedFormats);
	printf("  ---------------------------------\n");
	printf("  %-6s%-16s%-22s\n\n", "#", "Channel Order", "Channel Type");
	for (unsigned int i = 0; i < uiNumSupportedFormats; i++)
	{
		printf("  %-6u%-16s(%-8X)   %-22s(%-8X)\n", (i + 1),
			oclImageFormatString(ImageFormats[i].image_channel_order), ImageFormats[i].image_channel_order,
			oclImageFormatString(ImageFormats[i].image_channel_data_type), ImageFormats[i].image_channel_data_type);
	}
	printf("\n");
	delete[] ImageFormats;

	cl_mem_flags flags = CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR;

	cl_image_format format;
	format.image_channel_order = CL_RGBA;
	format.image_channel_data_type = CL_UNORM_INT8;
	
	cl_int error = 0;
	cl_mem A_e[20];
	//cl_mem A_d = clCreateImage2D(g_clContext, flags, &format, 1920, 1080, 1920*4, temp_video_buffer, &error);
	//cl_mem A_d = clCreateImage2D(g_clContext, flags, &format, 1920, 1080, 0, 0, &error);
	//clReleaseMemObject(A_d);
	for (int i = 0; i < 20; i++)
	{
		A_e[i] = clCreateImage2D(g_clContext, flags, &format, 1920, 1080, 1920 * 4, temp_video_buffer, &error);
		OPENCL_CHECK_ERRORS(error);
	}
	printf("clCreateImage2D Successful!!!\n");
	for (int i = 0; i < 20; i++)
	{
		clReleaseMemObject(A_e[i]);
	}

#endif



	if(numPlatforms > 0)
	{
		free(platforms);
	}

	if(g_clDevices != NULL)
	{
		free(g_clDevices);
	}

	return SUCCESS;
}

int GetDX11SharingFunctionsFromCL()
{
	int status = SUCCESS;  //TODO: clean up how status is managed

	ptrToFunction_clCreateFromD3D11Texture2DKHR = (clCreateFromD3D11Texture2DKHR_fn)clGetExtensionFunctionAddressForPlatform(g_platformToUse, "clCreateFromD3D11Texture2DKHR");
	if(ptrToFunction_clCreateFromD3D11Texture2DKHR == NULL)
	{
		status = FAIL;
	}
	
	ptrToFunction_clEnqueueAcquireD3D11ObjectsKHR = (clEnqueueAcquireD3D11ObjectsKHR_fn)clGetExtensionFunctionAddressForPlatform(g_platformToUse, "clEnqueueAcquireD3D11ObjectsKHR");
	if(ptrToFunction_clEnqueueAcquireD3D11ObjectsKHR == NULL)
	{
		status = FAIL;
	}
	
	ptrToFunction_clEnqueueReleaseD3D11ObjectsKHR = (clEnqueueReleaseD3D11ObjectsKHR_fn)clGetExtensionFunctionAddressForPlatform(g_platformToUse, "clEnqueueReleaseD3D11ObjectsKHR");
	if(ptrToFunction_clEnqueueReleaseD3D11ObjectsKHR == NULL)
	{
		status = FAIL;
	}

	return status;
}

int ExitCL()
{
	//cleanup all CL queues, contexts, programs, mem_objs
	cl_int status;

	if (g_SharedNV12imageCLMemObject_Y != NULL)
	{
		status = clReleaseMemObject(g_SharedNV12imageCLMemObject_Y);
		testStatus(status, "Error releasing mem object");
	}

	if (g_SharedNV12imageCLMemObject_UV != NULL)
	{
		status = clReleaseMemObject(g_SharedNV12imageCLMemObject_UV);
		testStatus(status, "Error releasing mem object 1");
	}


	status = clReleaseKernel(cl_kernel_drawBox_Y);
	testStatus(status, "Error releasing kernel");

	status = clReleaseKernel(cl_kernel_drawBox_UV);
	testStatus(status, "Error releasing kernel 1");

	status = clReleaseProgram(g_clProgram);
	testStatus(status, "Error releasing program");

	status = clReleaseCommandQueue(g_clCommandQueue);
	testStatus(status, "Error releasing command queue");

	status = clReleaseContext(g_clContext);
	testStatus(status, "Error releasing context");

	return status;
}
